package nina.yiyou.attendance.s10merge.input

import luj.groovy.excel.api.LugExcel
import luj.groovy.excel.api.table.Table
import luj.groovy.excel.api.table.body.Row
import luj.groovy.excel.api.table.header.Header
import luj.groovy.excel.api.table.header.HeaderField
import nina.yiyou.salary.input.util.ExcelReadOpener
import org.apache.poi.ss.usermodel.Sheet
import org.apache.poi.ss.usermodel.Workbook

import java.nio.file.Path
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import java.util.stream.Collectors

class AttendPunchExcelV2Reader {

  interface Line {

    /**
     * @return 员工姓名
     */
    String name()

    String department()

    LocalDate date()

    LocalDateTime punchIn()

    LocalDateTime punchOut()

    String shift()

    double workingHour()

    int forgotPunch()

    Set<String> extraArg()

    Map<String, Object> toMap()
  }

  AttendPunchExcelV2Reader(Path excelPath) {
    _excelPath = excelPath
  }

  List<Line> read() {
    Workbook workbook = ExcelReadOpener.get(_excelPath).openXlsx()
    List result = readLines(workbook.getSheetAt(0))
    workbook.close()
    return result
  }

  private List<Line> readLines(Sheet sheet) {
    Table table = LugExcel.table(sheet).header {
      startCell('A3')
      rowSize(2)
    }.build()

    return table.body.rows.stream()
        .map { makeLine(it, table.header) }
        .filter { it.shift() != '休息' }
        .collect(Collectors.toList())
  }

  private Line makeLine(Row row, Header header) {
    def getField = { String f ->
      HeaderField name = header.getField(f)
      assert name != null, "找不到列：${f}"
      return row.getFieldValue(name)
    }

    def name = (getField('姓名') as String).split('（', 2)[0]
    def department = getField('部门')
    
    def shift = getField('班次')
    def workingHour = readNum(getField('实际工作时长(小时)'))
    def forgotPunch = readNum(getField('假勤统计/补卡次数(次)')).round()

    def date = readDateV2(getField('时间') as String)
    def punchIn = resolvePunch(getField('最早'), date)
    def punchOut = resolvePunch(getField('最晚'), date)

    String argStr = ''// getField('额外参数')
    Set<String> extraArg = argStr ? Arrays.stream(argStr.split('\n'))
        .map { it.trim() }
        .collect(Collectors.toSet()) : []

    return new Line() {
      @Override
      String name() {
        return name
      }

      @Override
      String department() {
        return department
      }

      @Override
      LocalDate date() {
        return date
      }

      @Override
      LocalDateTime punchIn() {
        return punchIn
      }

      @Override
      LocalDateTime punchOut() {
        return punchOut
      }

      @Override
      String shift() {
        return shift
      }

      @Override
      double workingHour() {
        return workingHour
      }

      @Override
      int forgotPunch() {
        return forgotPunch
      }

      @Override
      Set<String> extraArg() {
        return extraArg
      }

      @Override
      Map<String, Object> toMap() {
        return header.fields.stream()
            .map { [it.name, row.getFieldValue(it)] }
            .collect(Collectors.toList())
            .collectEntries()
      }
    }
  }

  private double readNum(Object hour) {
    String hourStr = hour
    if (hourStr == '--') {
      return 0
    }
    return Double.parseDouble(hourStr)
  }

  private LocalDate readDate(Object date) {
    if (date instanceof LocalDate) {
      return date
    }
    return LocalDate.parse(date as String, DATE_FMT)
  }

  private LocalDate readDateV2(String timeStr) {
    String[] pair = timeStr.split('\\s+')
    assert pair.size() == 2
    return readDate(pair[0])
  }

  private LocalDateTime resolvePunch(Object time, LocalDate date) {
    if (time instanceof LocalDateTime) {
      return date.atTime(time.toLocalTime())
    }

    //TODO: 不允许<未打卡>
    def timeStr = time as String
    if (timeStr == '未打卡' || timeStr == '--') {
      return null
    }

    if (timeStr.startsWith('次日')) {
      LocalTime newTime = LocalTime.parse(timeStr.substring(2), TIME_FMT)
      return date.plusDays(1).atTime(newTime)
    }

    LocalTime newTime = LocalTime.parse(timeStr, TIME_FMT)
    return date.atTime(newTime)
  }

  private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy/MM/dd")
  private static final DateTimeFormatter TIME_FMT = DateTimeFormatter.ofPattern('HH:mm')

  private final Path _excelPath
}
